package org.smart.framework.core.helper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart.framework.core.annotation.Aspect;
import org.smart.framework.core.annotation.Service;
import org.smart.framework.core.prox.Proxy;
import org.smart.framework.core.prox.ProxyManager;
import org.smart.framework.core.prox.TransactionProxy;

import java.lang.annotation.Annotation;
import java.util.*;

/**
 * @author lyric
 * @version 1.0.0
 * @date 2017/2/12 12:28
 * @description 方法拦截助手类
 */
public final class AopHelper {

    private static final Logger logger = LoggerFactory.getLogger(AopHelper.class);

    static {
        try {
            Map<Class<?>, Set<Class<?>>> proxyMap = createProxyMap();
            Map<Class<?>, List<Proxy>> targetMap = createTargetMap(proxyMap);
            for (Map.Entry<Class<?>, List<Proxy>> targetEnty : targetMap.entrySet()) {
                Class<?> targetClass = targetEnty.getKey();
                List<Proxy> proxies = targetEnty.getValue();
                Object proxy = ProxyManager.createProxy(targetClass, proxies);
                BeanHelper.setBean(targetClass, proxy);
             }
        } catch (Exception e) {
            logger.error("aop failure", e);
        }
    }

    /**
     * @author lyric
     * @description 创建目标类集合
     * @param aspect 注解类
     * @return
     * @throws Exception
     */
    private static Set<Class<?>> createTargetClassSet(Aspect aspect) throws Exception {
        Set<Class<?>> targetClassSet = new HashSet<Class<?>>();
        Class<? extends Annotation> anotation = aspect.value();
        if (null != anotation && !anotation.equals(Aspect.class)) {
            targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(anotation));
        }
        return targetClassSet;
    }

    /**
     * @author lyric
     * @description 创建代理类与目标类集合之间的映射关系
     * @return
     * @throws Exception
     */
    private static Map<Class<?>, Set<Class<?>>> createProxyMap() throws Exception {
        Map<Class<?>, Set<Class<?>>> proxy = new HashMap<Class<?>, Set<Class<?>>>();
        addAspectProxy(proxy);
        addTransactionProxy(proxy);
        return proxy;
    }

    /**
     * @author lyric
     * @description 创建代理类列表与目标类之间的映射关系
     * @param proxyMap
     * @return
     * @throws Exception
     */
    private static Map<Class<?>, List<Proxy>> createTargetMap(Map<Class<?>, Set<Class<?>>> proxyMap) throws Exception {
        Map<Class<?>, List<Proxy>> targetMap = new HashMap<Class<?>, List<Proxy>>();
        for (Map.Entry<Class<?>, Set<Class<?>>> proxyEntry : proxyMap.entrySet()) {
            Class<?> proxyClass = proxyEntry.getKey();
            Set<Class<?>> targetClassSet = proxyEntry.getValue();
            for (Class<?> targetClass : targetClassSet) {
                Proxy proxy = (Proxy) proxyClass.newInstance();
                if (targetMap.containsKey(targetClass)) {
                    targetMap.get(targetClass).add(proxy);
                } else {
                    List<Proxy> proxies = new ArrayList<Proxy>();
                    proxies.add(proxy);
                    targetMap.put(targetClass,proxies);
                }
            }
        }
        return targetMap;
    }

    /**
     * @author lyric
     * @description 添加拦截代理映射关系
     * @param proxy
     * @throws Exception
     */
    private static void addAspectProxy(Map<Class<?>, Set<Class<?>>> proxy) throws Exception {
        Set<Class<?>> proxyClasses = ClassHelper.getClassSetBySuper(Aspect.class);
        for (Class<?> proxyClass : proxyClasses) {
            if (proxyClass.isAnnotationPresent(Aspect.class)) {
                Aspect aspect = proxyClass.getAnnotation(Aspect.class);
                Set<Class<?>> targetClassSet = createTargetClassSet(aspect);
                proxy.put(proxyClass, targetClassSet);
            }
        }
    }

    /**
     * @author lyric
     * @description 添加事务代理映射关系
     * @param proxy
     * @throws Exception
     */
    private static void addTransactionProxy(Map<Class<?>, Set<Class<?>>> proxy) {
        Set<Class<?>> serviceClassSet = ClassHelper.getClassSetByAnnotation(Service.class);
        proxy.put(TransactionProxy.class, serviceClassSet);
    }
}
